/******************************************************************************
This file is part of AppKit.
Project: appkit
Author : FergusZeng
Email  : cblock@126.com
git	   : https://gitee.com/newgolo/appkit.git
*******************************************************************************
MIT License

Copyright (c) 2022 cblock@126.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#pragma once

/**
 *  @file   system.h
 *  @brief  进程工具类.
 */
#include <iostream>
#include <string>
#include <vector>

#include "appkit/basetype.h"
#include "appkit/fileio.h"
#include "appkit/strutil.h"
#include "appkit/thread.h"
namespace appkit {
/**
 * @class System
 * @brief 系统工具类
 */
class System {
public:
    System() {}
    ~System() {}
    /**
     * @brief 生成伪随机数
     * @param max 最大值
     * @return int 生成的伪随机数
     */
    static int random(int max);
    /**
     * @brief 生成UUID
     * @return std::string
     */
    static std::string uuid(void);
    /**
     * @brief 执行命令
     * @param cmd
     * @return int int 成功返回RC_OK,失败返回RC_ERROR
     * @note 内部使用system实现,函数会阻塞直到cmd执行完
     */
    static int execute(const std::string& cmd);
    /**
     * @brief 执行命令
     * @param cmd 命令
     * @param resultStr 命令输出结果
     * @param msTimeout 超时时间(-1:等待进程退出,其他值:最长等待时间)
     * @return int 成功返回RC_OK,错误返回RC_ERROR,超时返回RC_TIMEOUT
     */
    static int execute(const std::string& cmd, std::string* resultStr,
                       int msTimeout = -1);

    /**
     * @brief 进程退出
     * @param code 退出错误码
     * @param force 强制退出(不清理IO缓存)
     */
    static void exit(int code = 0, bool force = false);

    /**
     * @brief 启动进程
     * @param procName 进程名
     * @param args 参数
     * @param envs 环境变量
     * @param delays 延时时间
     * @return int 成功返回子进程pid,否则返回RC_ERROR
     */
    static int spawnOne(const std::string& procName, char** args,
                        int delays = 0);
    /**
     * @brief 停止除自身外的进程(杀死进程)
     * @param procName 进程名
     */
    static void killOther(const std::string& procName);
    /**
     * @brief 杀死进程组(父进程及所有子进程)
     */
    static void killGroup(const std::string& procName);
    /**
     * @brief 获取进程PID
     * @param processName 进程名称
     * @return std::vector<int> 进程PID数组
     */
    static std::vector<int> getPidsByName(const std::string& processName);
    /**
     * @brief 获取环境变量
     * @param name 环境变量名称
     * @return std::string
     */
    static std::string getEnv(const std::string& name);
    /**
     * @brief 设置环境变量
     * @param name 环境变量名称
     * @param value 环境变量值
     */
    static void setEnv(const std::string& name, const std::string& value);
    /**
     * @brief 设置进程亲和性(将当前进程/线程绑定到指定的cpu)
     * @param cpuIds 取值0~n,代表CPU0~CPUn
     * @return true
     * @return false
     */
    static bool setAffinity(std::vector<int> cpuIds);
    /**
     * @brief 获取进程占用的内存
     * @param name 进程名称
     * @return int
     */
    static int getProcMemory(const std::string& name);
    /**
     * @brief 重启系统
     * @return true
     * @return false
     */
    static bool reboot();
    /**
     *  @brief  初始化实时抢占系统(需在进程开始处调用)
     *  @param  policy
     *  @param  priority
     *  @return 成功返回true,失败返回false
     *  @note   此函数仅限在linux-rt上使用
     */
    static bool setPreemptRT(int policy, int priority);
};

/**
 * @class SysStat
 * @brief 系统统计
 */
class SysStat : public Runnable {
    DECL_CLASSMETA(SysStat)
    struct CpuStat {
        char name[20];
        uint32 user{0};
        uint32 nice{0};
        uint32 system{0};
        uint32 idle{0};
        uint32 iowait{0};
        uint32 irq{0};
        uint32 softirq{0};
    };

public:
    SysStat();
    ~SysStat();
    /**
     * @brief 开始统计
     * @param msInterval 统计间隔时间(ms)
     * @param cupId (-1:所有CPU,0~n:单CPU)
     * @param statCallback (统计回调)
     * @return true
     * @return false
     */
    bool start(int cpuId = -1, uint32 msInterval = 1000,
               std::function<void(void)> statCallback = nullptr);
    /**
     * @brief 停止统计
     */
    void stop();
    /**
     * @brief 获取CPU使用率
     */
    float getCpuUsage();
    /**
     * @brief 获取系统内存大小(kbytes)
     * @return uint32
     */
    uint32 getMemTotal();
    /**
     * @brief 获取系统内存空闲量(kbytes)
     * @return uint32
     */
    uint32 getMemFree();

private:
    void run(const Thread& thread) override;
    void updateCpuStat();
    void updateMemStat();

private:
    Thread m_thread{"SysStat"};
    uint32 m_interval{1000};
    int m_cpuId{-1};
    std::function<void(void)> m_statCallback{nullptr};
    CpuStat m_lastCpuStat;
    CpuStat m_currCpuStat;
    uint32 m_memTotalKB{0};
    uint32 m_memFreeKB{0};
};

/**
 * @class Endian
 * @brief 大小端工具类
 */
class Endian {
public:
    /**
     * @brief 判断本机是否是大端字节序
     * @return true 是
     * @return false 不是
     */
    static bool isBigEndian(void);
    /**
     * @brief 16位整数转换为小端字节序
     * @param value 16位整数
     * @return uint16 小端16位整数
     */
    static uint16 host2LitEndian(uint16 value);
    /**
     * @brief 32位整数转换为小端字节序
     * @param value 32位整数
     * @return uint32 小端32位整数
     */
    static uint32 host2LitEndian(uint32 value);
    /**
     * @brief 32位浮点数转换为小端字节序
     * @param value 32位浮点数
     * @return float 小端32位浮点数
     */
    static float host2LitEndian(float value);
    /**
     * @brief 16位整数转换为大端字节序
     * @param value 16位整数
     * @return uint16 大端16位整数
     */
    static uint16 host2BigEndian(uint16 value);
    /**
     * @brief 32位整数转换为大端字节序
     * @param value 32位整数
     * @return uint32 大端32位整数
     */
    static uint32 host2BigEndian(uint32 value);
    /**
     * @brief 32位浮点数转换为大端字节序
     * @param value 32位浮点数
     * @return float 大端32位浮点数
     */
    static float host2BigEndian(float value);
    /**
     * @brief unicode转换位utf-8字符串
     * @param unicode unicode编码
     * @return string utf-8字符串(1~6字节)
     */
    static std::string unicodeOneToUtf8String(uint32 unicode);
    /**
     * @brief utf-8字符串转换位unicode编码
     * @param utf8code utf-8字符串(1~6字节)
     * @return uint32 unicode编码
     */
    static uint32 utf8OneToUnicode(const char* utf8code);
    /**
     * @brief 位反转
     * @param value 值
     * @param bits 要反转的位数(从最低位开始)
     * @return uint32 位反转后的值
     */
    static uint32 bitsReverse(uint32 value, uint8 bits);
};

class FileWriteProtection {
    DECL_CLASSMETA(FileWriteProtection)
public:
    explicit FileWriteProtection(const std::string& filePath);
    ~FileWriteProtection();

private:
    std::string m_filePath;
};

}  // namespace appkit
